home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
4_0
/
TERMINAL
/
SRCS
/
XMODEM.C
< prev
next >
Wrap
Text File
|
1990-11-20
|
26KB
|
1,071 lines
/*
Terminal 2.0
"XModem.c"
*/
#ifdef THINK_C
#include "MacHeaders"
#endif
#ifdef applec
#pragma load ":(Objects):MacHeadersMPW"
#pragma segment XModem
#endif
#include "XModem.h"
#include "CRC.h"
#include "Strings.h"
#include "Utilities.h"
#include "CancelDialog.h"
#include "Serial.h"
#include "Text.h"
#include "Main.h"
#include "MacBinary.h"
#include "Document.h"
/* #define MONITOR */
#ifdef MONITOR
#include "Monitor.h"
#include "FormatStr.h"
#endif
#define SOH 0x01
#define STX 0x02
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define CAN 0x18
#define LONG Settings.XModemtimeout /* Timeout between blocks */
#define SHORT LONG/4 /* Timeout between bytes */
#define WAITMAX LONG*10 /* While ignoring ... */
#define RETRY 10 /* Normal retries */
#define RETRY_CRC RETRY/2 /* Negociating retries */
#define TIME ((eof && mark) ? ((Time-time)*(eof-mark))/mark : 0)
#define XModemStr MyString(STR_P, P_XMODEM)
#define YModemStr MyString(STR_P, P_YMODEM)
#define CRCStr MyString(STR_P, P_CRC)
#define CKSMStr MyString(STR_P, P_CHECKSUM)
#define Blk1KStr MyString(STR_P, P_1K)
#define TextStr MyString(STR_P, P_TEXT)
#define Bin1Str MyString(STR_P, P_BINARY1)
#define Bin2Str MyString(STR_P, P_BINARY2)
#define TimeoutStr MyString(STR_P, P_TIMEOUT)
#define InvalidStr MyString(STR_P, P_INVALID)
#define RetryStr MyString(STR_P, P_REPEAT)
#define Block1Str MyString(STR_P, P_BLOCKERR1)
#define Block2Str MyString(STR_P, P_BLOCKERR2)
#define ChecksumStr MyString(STR_P, P_CRCERR)
#define VerifyStr MyString(STR_P, P_VERIFY)
#define EndStr MyString(STR_P, P_END)
#define ReverifyStr MyString(STR_P, P_REVERIFY)
#define NegociateStr MyString(STR_P, P_NEGOCIATE)
typedef union {
unsigned short w;
Byte b[2];
} CRC;
typedef struct {
short n; /* Data bytes */
Byte b[3+1024+2]; /* Block buffer */
} BLOCK;
/* ----- Extract file info from batch header block --------------------- */
static void ExtractFileInfo(
register Byte *buffer,
Byte *name,
long *size)
{
register Byte *p;
register Byte *max = buffer + 128;
register Byte c;
Byte number[128];
*name = 0;
p = name + 1;
while (buffer < max) {
c = *buffer++;
if (c) {
*p++ = c;
++(*name);
} else
break;
}
*number = 0;
p = number + 1;
while (buffer < max) {
c = *buffer++;
if (c && c != ' ') {
*p++ = c;
++(*number);
} else
break;
}
StringToNum(number, size);
}
/* ----- Set up batch header block with file info ---------------------- */
static void MakeFileInfo(
register Byte *buffer,
register Byte *name,
register long size)
{
register Byte *max = buffer + 128;
register short n;
register Byte s[20];
if (size) {
if (n = *name) {
memcpy(buffer, name + 1, n);
buffer += n;
}
*buffer++ = 0;
NumToString(size, s);
memcpy(buffer, s + 1, n = *s);
buffer += n;
}
while(buffer < max)
*buffer++ = 0;
}
/* ----- Make info string for progress dialog -------------------------- */
static void MakeInfo(
register Boolean crc, /* CRC option */
register Boolean k, /* 1K option */
register short vers) /* MacBinary version */
{
register Byte s[256];
if (Settings.batch)
memcpy(s, YModemStr, YModemStr[0] + 1);
else
memcpy(s, XModemStr, XModemStr[0] + 1);
if (crc)
Append(s, CRCStr);
else
Append(s, CKSMStr);
if (k)
Append(s, Blk1KStr);
switch(vers) {
case 0:
Append(s, TextStr);
break;
case 1:
Append(s, Bin1Str);
break;
case 2:
Append(s, Bin2Str);
break;
}
InfoProgress(s);
}
/* ----- Calculate checksum -------------------------------------------- */
static Byte Checksum(
register Byte *buffer,
register short length)
{
register Byte cksm = 0;
while(length--)
cksm += *buffer++;
return cksm;
}
/* ----- Receive character with timeout -------------------------------- */
static short Receive(
register Byte *character,
register long ticks)
{
register long start;
start = Ticks;
while ((Ticks - start) < ticks) {
if (CheckCancel())
return CANCEL;
if (SerialRead(character, 1))
return FINE;
}
return TIMEOUT;
}
/* ----- Ignore characters until timeout ------------------------------- */
static short Ignore(register long timeout)
{
register short err;
register long start;
Byte c;
start = Ticks;
while ((err = Receive(&c, timeout)) == FINE)
/* Be sure not to ignore incoming characters forever! */
if ((Ticks - start) > WAITMAX)
return CANCEL;
return err; /* CANCEL or TIMEOUT */
}
/* ----- Receive characters with timeout ------------------------------- */
static short ReceiveBuffer(
register Byte *buffer,
register long count,
register long ticks)
{
register long start;
register short err;
ticks += SerialDuration(Settings.portSetup, count);
start = Ticks;
while ((Ticks - start) < ticks) {
if (CheckCancel()) {
err = CANCEL;
goto done;
}
if (SerialCheck() >= count) {
SerialFastRead(buffer, count);
err = FINE;
goto done;
}
}
err = TIMEOUT;
done:
return err;
}
/* ----- XModem receive ------------------------------------------------ */
enum {
STATE_NAK, /* NAKed a data block */
STATE_ACK, /* ACKed data block */
STATE_ACK2, /* re-ACKed a data block */
STATE_EOT, /* ACKed EOT */
STATE_BLK0, /* ACKed header block (batch) */
STATE_END /* ACKed header block, file name length is 0 (batch) */
};
short XReceive(
Byte *fname, /* File name */
short volume, /* Volume reference number */
long directory) /* Directory id */
{
Byte buffer[3+1024+2]; /* Block buffer */
Byte *message; /* Message for progress dialog */
short limit; /* Maximum size of buffer */
short dataLength; /* 128 or 1024 */
short err; /* Error code */
Byte expected; /* Current block number */
short retry; /* Retry counter */
long mark; /* Current file mark */
long blk; /* Block number expected */
long time; /* Starting time */
long count; /* Current length of block buffer */
short offset; /* Data offset in buffer */
Byte c; /* Character to transmit */
Byte ck[3]; /* Characters to transmit */
CRC crc; /* Checksum or CRC */
short ref; /* File reference number */
Byte name1[128]; /* Original name */
Byte name2[128]; /* Name from MacBinary header */
long eof; /* File length */
long size; /* File size from batch header */
short vers; /* MacBinary version */
Byte *usedName; /* File name used */
Boolean useCRC; /* CRC option */
short use1K; /* 1K option */
short canceled = 0; /* Used if CAN received */
Boolean open; /* File to write into is open */
Boolean open2; /* File to write into is open */
short state; /* Current state */
#ifdef MONITOR
Byte mon[255];
#endif
if (Sending || Transfer)
return fBsyErr;
Transfer = Transfer_Rx;
SetItemStyle(GetMenu(FILE), RECEIVE, ACTIVE);
SerialBinary(Settings.portSetup);
if (Settings.handshake == 1) /* XON/OFF must be off */
SerialHandshake(0);
*name1 = 0;
if (Settings.batch) {
volume = Settings.volume;
directory = Settings.directory;
} else
if ((Byte)*fname > 0)
memcpy(name1, fname, *fname + 1);
DrawProgressDialog(P_RFILE, name1);
#ifdef MONITOR
MonitorOpen("\pRECEIVE.DUMP", Settings.volume, Settings.directory);
MonitorText("\pStart of X-Modem receive");
#endif
/* Goto here if during batch file transfer a file has been successfully
received and saved. */
newfile:
message = EmptyStr;
blk = expected = Settings.batch ? 0 : 1;
mark = eof = size = 0;
vers = ref = 0;
open = open2 = FALSE;
UpdateProgress(0,0,0,0,0,EmptyStr);
InfoProgress(EmptyStr);
state = STATE_BLK0;
time = Time;
/* Goto here if during batch file transfer the header block was
received and ACKed, and file name extracted. */
negociate:
useCRC = Settings.XModemCRC;
use1K = Settings.XModem1K;
if (useCRC) {
retry = RETRY_CRC + 1;
c = 0; /* Use string in ck[] */
ck[1] = 'C';
if (use1K == 2) { /* "CK" CRC and 1K option */
ck[2] = 'K';
ck[0] = 2;
} else /* "C" CRC option, automatic 1K */
ck[0] = 1;
} else {
ck[0] = 0;
retry = RETRY;
c = NAK; /* Use byte in c */
}
/* Goto to here if a NAK (or "C", "CK") must be sent. */
repeat:
/* Make sure line is cleared if NAKing (if not previous CAN) */
if (!canceled && state == STATE_NAK &&
((err = Ignore(SHORT)) == CANCEL))
goto finished;
repeat1:
state = STATE_NAK;
if (retry) {
retry--;
if (c)
c = NAK;
} else { /* Retry count exhausted */
if (c) {
err = TIMEOUT; /* This is a real error */
goto finished;
}
useCRC = use1K = FALSE; /* Check for CRC/1K failed */
retry = RETRY;
c = NAK;
}
/* Goto here if an ACK must be sent. */
transmit:
if (c)
SerialSend(&c, 1, &Busy); /* NAK or ACK */
else
SerialSend(ck + 1, ck[0], &Busy); /* "C" or "CK" */
#ifdef MONITOR
if (c)
FormatStr(mon, "\pTransmit %i", c);
else
FormatStr(mon, "\pTransmit '%s'", ck);
MonitorText(mon);
#endif
/* By putting UpdateProgress() and disk writes here, the Mac is
already receiving the next block while we do these time consuming
operations: this makes the file transfer much much faster! */
UpdateProgress(mark,eof,TIME,blk,(c?RETRY:RETRY_CRC)-retry,message);
while (Busy) /* Wait until SerialSend() finished */
;
/* Dispatcher: see what must be done after our transmission */
switch (state) {
case STATE_EOT: /* ACKed EOT */
err = FINE;
goto finished;
case STATE_END: /* ACKed final header block (batch) */
err = FINE;
goto done;
case STATE_BLK0: /* ACKed header block (batch) */
expected++; /* Modulo 256 */
blk++;
if (Settings.batch == 1) /* "Official" Y-Modem batch */
goto negociate;
else /* Red Ryder interpretation */
break;
case STATE_NAK: /* NAKed received block */
case STATE_ACK2: /* Re-ACKed data block */
break;
case STATE_ACK: /* ACKed data block */
if (count > 0 && (err = vers ?
BinWrite(&count, buffer + offset) :
FSWrite(ref, &count, buffer + offset)))
goto finished; /* Write errror */
expected++; /* Modulo 256 */
blk++;
#ifdef MONITOR
FormatStr(mon, "\pExpecting block #%i", expected);
MonitorText(mon);
#endif
break;
}
/* Wait for 1st byte of next block */
err = Receive(buffer, LONG);
#ifdef MONITOR
if (err)
MonitorText("\pFirst byte timeout");
else {
FormatStr(mon, "\pFirst byte %i", *buffer);
MonitorText(mon);
}
#endif
if (err) {
if (err == CANCEL)
goto finished;
message = TimeoutStr;
goto repeat1; /* Transmit NAK (or "C", "CK") */
}
/* We just got one byte. Only EOT, SOH/STX or CAN are accepted. */
if (buffer[0] == CAN)
if (canceled) {
err = ABORT; /* 2 consecutive CANs ==> abort */
goto finished;
} else
++canceled;
else
canceled = 0;
if (buffer[0] == EOT) {
state = STATE_EOT;
retry = RETRY;
c = ACK;
goto transmit;
}
/* SOH ==> 128 bytes, STX ==> 1024 bytes per block */
if (buffer[0] != SOH && !(use1K && buffer[0] == STX)) {
message = InvalidStr;
goto repeat; /* Transmit NAK (or "C", "CK") */
}
limit = (dataLength=(buffer[0]==SOH)?128:1024) + (useCRC?5:4);
/* Just received SOH/STX, now get all the rest. */
err = ReceiveBuffer(buffer + 1, limit - 1, LONG);
#ifdef MONITOR
if (err)
MonitorText("\pBlock timeout");
else {
MonitorText("\pBlock received");
MonitorDump(buffer, limit);
}
#endif
if (err) {
if (err == CANCEL)
goto finished;
message = TimeoutStr;
goto repeat1; /* Transmit NAK (or "C", "CK") */
}
/* Verify the block complement and checksum received. */
if (buffer[1] + buffer[2] != 0xFF) {
message = Block1Str;
goto repeat; /* Transmit NAK (or "C", "CK") */
}
if (useCRC) { /* 2 byte CRC */
crc.b[0] = buffer[limit - 2];
crc.b[1] = buffer[limit - 1];
err = (crc.w != CalcCRC(buffer + 3, dataLength, 0));
} else /* 1 byte checksum */
err = (buffer[limit - 1] != Checksum(buffer + 3, dataLength));
if (err) {
message = ChecksumStr;
goto repeat; /* Transmit NAK (or "C", "CK") */
}
/* Are we expecting this block ? */
if (buffer[1] == expected) { /* Expected block number */
if (!c) /* Initial handshake is finished */
MakeInfo(useCRC, use1K, 0);
message = VerifyStr;
count = dataLength;
offset = 3;
if (blk == 0) { /* Batch header block */
/* Extract the file name from the Y-Modem header block. If a
file with that name exists it is deleted. An empty name signals
the end of the batch transfer. */
ExtractFileInfo(buffer + 3, name1, &size);
eof = size + Filler(128, size);
NameProgress(name1);
if (*name1 == 0)
state = STATE_END;
else {
state = STATE_BLK0;
DeleteFile(volume, directory, name1);
}
} else { /* Not batch header block */
state = STATE_ACK;
if (blk == 1) {
/* Try to use the file name from the MacBinary header. If
this file exists already use the first name. */
if (Settings.Binary) { /* MacBinary check */
long df, rf;
vers = BinCheckHeader(buffer + 3, name2, &df, &rf);
}
if (vers) {
err = CreateFile(volume, directory,
usedName = name2,
Settings.binCreator, Settings.binType);
if (err == dupFNErr)
err = CreateFile(volume, directory,
usedName = name1,
Settings.binCreator, Settings.binType);
} else
err = CreateFile(volume, directory,
usedName = name1,
Settings.binCreator, Settings.binType);
if (err)
goto finished; /* Create error */
mark += count;
if (vers) { /* Valid MacBinary header */
NameProgress(usedName);
MakeInfo(useCRC, use1K, vers);
if (err = BinOpenWrite(volume, directory, usedName,
buffer + 3))
goto finished; /* Open error */
open2 = TRUE;
BinGetEOF(&eof);
/* Skip MacBinary header, but write the rest. (actual
write only after ACK has been sent) */
count -= BinHeaderLength;
offset += BinHeaderLength;
} else { /* Not MacBinary */
if (err = OpenFile(volume,directory,usedName,&ref))
goto finished; /* Open error */
open2 = TRUE;
/* The first block is no MacBinary header, so it must
be written as data block (actual write only after ACK
has been sent) */
}
} else /* Not the first data block */
open = TRUE;
if (open) { /* Not the first data block */
mark += count;
/* (actual write only after ACK has been sent) */
}
/* If file size is known, make correction */
if (size && mark > size) {
count -= mark - size;
}
} /* (Not batch header block) */
} else { /* Not the expected block number */
Byte previous = expected;
--previous; /* Modulo 256 */
if (buffer[1] != previous) {
message = Block2Str; /* Lost synchronization */
goto repeat; /* Transmit NAK (or "C", "CK") */
}
message = ReverifyStr;
state = STATE_ACK2;
}
retry = RETRY;
c = ACK;
goto transmit; /* Transmit ACK */
/* Goto here if transfer of single file is finished, because EOT has
been ACKed (err == 0) or because there was an error (err != 0). */
finished:
time = Time - time;
if (open2) { /* If file is open, then close it */
if (vers)
BinCloseWrite();
else
FSClose(ref);
if (err) /* If error delete uncomplete file */
DeleteFile(volume, directory, usedName);
}
Statistics(mark, time, err);
FlushVol(0, volume);
if (!err && Settings.batch) { /* Try again if batch file transfer */
NameProgress(EmptyStr);
goto newfile;
}
/* Goto here if batch header block has zero length file name, meaning
that the batch file transfer is over. */
done:
#ifdef MONITOR
MonitorText("\pEnd of X-Modem receive");
MonitorClose();
#endif
RemoveCancelDialog();
SerialReset(Settings.portSetup);
SerialHandshake(Settings.handshake);
SetItemStyle(GetMenu(FILE), RECEIVE, 0);
Transfer = 0;
return err;
}
/* ----- Create data block --------------------------------------------- */
#define LEVEL 896 /* 7 128-Byte blocks */
static short ReadFile(
register BLOCK *filebuf,
short ref,
Boolean useCRC,
short use1K,
short vers,
register Byte *block,
long *size,
long *mark)
{
long count;
register short err;
CRC crc;
if (*size) { /* Still some data left? */
if (use1K && *size > LEVEL) {
filebuf->n = 1024;
filebuf->b[0] = STX;
} else {
filebuf->n = 128;
filebuf->b[0] = SOH;
}
filebuf->b[1] = *block;
filebuf->b[2] = 0xFF - *block;
++(*block);
count = filebuf->n; /* Next block from file */
if (vers)
err = BinRead(&count, filebuf->b + 3);
else
err = FSRead(ref, &count, filebuf->b + 3);
if (err && err != eofErr)
return err;
*mark += filebuf->n;
*size -= count;
while(count < filebuf->n) { /* Last (partial) block */
filebuf->b[count + 3] = 0;
count++;
}
if (useCRC) {
crc.w = CalcCRC(filebuf->b + 3, filebuf->n, 0);
filebuf->b[filebuf->n + 3] = crc.b[0];
filebuf->b[filebuf->n + 4] = crc.b[1];
filebuf->n += 3 + 2;
} else {
filebuf->b[filebuf->n + 3] =
Checksum(filebuf->b + 3, filebuf->n);
filebuf->n += 3 + 1;
}
} else {
filebuf->b[0] = EOT;
filebuf->n = 1;
}
return noErr;
}
/* ----- Create batch header block ------------------------------------- */
static void HeaderBlock(
register BLOCK* sendbuf,
Byte *name,
long eof,
Boolean useCRC)
{
CRC crc;
MakeFileInfo(sendbuf->b + 3, name, eof);
sendbuf->n = 128;
sendbuf->b[0] = SOH;
sendbuf->b[1] = 0x00;
sendbuf->b[2] = 0xFF;
if (useCRC) {
crc.w = CalcCRC(sendbuf->b + 3, sendbuf->n, 0);
sendbuf->b[sendbuf->n + 3] = crc.b[0];
sendbuf->b[sendbuf->n + 4] = crc.b[1];
sendbuf->n += 3 + 2;
} else {
sendbuf->b[sendbuf->n + 3] =
Checksum(sendbuf->b + 3, sendbuf->n);
sendbuf->n += 3 + 1;
}
}
/* ----- Negociate with remote receiver -------------------------------- */
enum {
TX_NEGOCIATE1, /* Waiting for "C" */
TX_NEGOCIATE2 /* Waiting for "K" */
};
static short Negociate(
register Boolean *useCRC,
register short *use1K)
{
register short err;
register short error;
register short timeout;
register short state;
Byte c;
short canceled = 0;
InfoProgress(NegociateStr);
error = 0;
state = TX_NEGOCIATE1;
timeout = LONG;
while (TRUE) {
if (error >= RETRY)
return TIMEOUT;
if ((err = Receive(&c, timeout)) == CANCEL)
return CANCEL;
if (!err && c == CAN)
if (canceled) /* 2 consecutive CANs ==> abort */
return ABORT;
else {
++canceled;
continue;
}
else
canceled = 0;
#ifdef MONITOR
if (err)
MonitorText("\pNegociating timeout");
else {
Byte mon[256];
FormatStr(mon, "\pNegociating received %i", c);
MonitorText(mon);
}
#endif
switch (state) {
case TX_NEGOCIATE1:
if (err) { /* Timeout */
timeout = LONG;
++error;
}
switch (c) {
case 'C': /* CRC option */
if (Settings.XModemCRC) {
*useCRC = TRUE;
if ((*use1K = Settings.XModem1K) == 2) {
timeout = SHORT;
state = TX_NEGOCIATE2;
} else
return FINE;
} else {
timeout = LONG;
++error;
if (Ignore(SHORT) == CANCEL)
return CANCEL;
}
break;
case NAK: /* No options */
*useCRC = *use1K = FALSE;
return FINE;
default: /* Unrecognized */
timeout = LONG;
++error;
if (Ignore(SHORT) == CANCEL)
return CANCEL;
}
break;
case TX_NEGOCIATE2:
if (err) /* Timeout, only "C" was received */
return FINE;
if (c == 'K') /* "CK" was received */
return FINE;
/* Unrecognized */
timeout = LONG;
++error;
if (Ignore(SHORT) == CANCEL)
return CANCEL;
state = TX_NEGOCIATE1;
break;
}
}
}
/* ----- XModem transmit ----------------------------------------------- */
enum {
TX_HDR,
TX_DATA, /* Data transfer in progress */
TX_EOT /* EOT is in transmit buffer */
};
short XTransmit(
Byte *name, /* File name */
short volume, /* Volume reference numbere */
long directory) /* Directpry id */
{
BLOCK buf1, buf2; /* Block buffers */
BLOCK *sendbuf, *filebuf; /* Send buffer, file buffer */
Byte *message; /* Message for progress dialog */
short err; /* Error code */
long mark; /* Current file mark */
long blk; /* Current block number */
long time; /* Start time */
long error; /* Error counter */
short ref; /* File reference number */
Byte c; /* Character received */
long eof; /* Length of file */
short vers; /* MacBinary version */
Boolean useCRC; /* CRC option */
short use1K; /* 1K option */
Byte block; /* Block counter (modulo 256) */
short canceled; /* CAN counter */
long size; /* Bytes remaining to be sent */
short state; /* Current state */
Boolean final = FALSE; /* If file has been transmited */
OSType creator, type;
long create, modif;
if (Sending || Transfer)
return fBsyErr;
/* Only send non-TEXT files as MacBinary if requested. TEXT files are
never sent as MacBinary. */
vers = (Settings.Binary &&
!InfoFile(volume, directory, name,
&creator, &type, &create, &modif) &&
type != TEXT) ? 2 : 0;
if (err = (vers ? BinOpenRead(volume, directory, name) :
OpenFile(volume, directory, name, &ref)))
return err;
/* Prepare file transfer. In batch mode only one file is sent, even if
the protocol allows for several files to be sent in one session. */
Transfer = Transfer_Tx;
SetItemStyle(GetMenu(FILE), TRANSMIT, ACTIVE);
SerialBinary(Settings.portSetup);
if (Settings.handshake == 1) /* XON/OFF must be off */
SerialHandshake(0);
if (vers)
BinGetEOF(&size);
else
GetEOF(ref, &size); /* Exact file size (data fork) */
eof = size + Filler(128, size); /* Multiple of 128 bytes */
mark = 0;
DrawProgressDialog(P_TFILE, name);
UpdateProgress(0,eof,0,0,0,EmptyStr);
sendbuf = &buf1;
filebuf = &buf2;
#ifdef MONITOR
MonitorOpen("\pTRANSMIT.DUMP", Settings.volume, Settings.directory);
MonitorText("\pStart of X-Modem transmit");
#endif
/* Start by negociating */
err = Negociate(&useCRC, &use1K);
time = Time;
if (err)
goto finished;
MakeInfo(useCRC, use1K, vers);
/* Prepare first block to transmit */
if (Settings.batch) {
blk = 0;
HeaderBlock(sendbuf, name, eof, useCRC);
block = 1;
state = TX_HDR;
} else {
blk = block = 1;
if (err = ReadFile(sendbuf, ref, useCRC, use1K, vers,
&block, &size, &mark))
goto finished;
state = TX_DATA;
}
message = EmptyStr;
error = 0;
do {
/* If repeating the block, then first wait for the line to clear */
if (error && (err = Ignore(SHORT)) == CANCEL)
goto finished;
/* Transmit data block (or EOT) */
SerialSend(sendbuf->b, sendbuf->n, &Busy);
if (state == TX_DATA && sendbuf->n == 1 && sendbuf->b[0] == EOT)
state = TX_EOT;
#ifdef MONITOR
MonitorText("\pTransmitting block");
MonitorDump(sendbuf->b, sendbuf->n);
#endif
/* We update the progress report and do disk file reads while the
Mac is sending the block. This increases the transfer speed! */
UpdateProgress(mark, eof, TIME, blk, error, message);
if (!error &&
((state == TX_HDR && !final) || state == TX_DATA)) {
if (err = ReadFile(filebuf, ref, useCRC, use1K, vers, &block,
&size, &mark))
goto finished;
}
while(Busy) /* Wait until transmission is finished */
;
/* Receive next character */
canceled = 0;
do {
if ((err = Receive(&c, canceled ? SHORT : LONG)) == CANCEL)
goto finished;
#ifdef MONITOR
if (err)
MonitorText("\pTimeout");
else {
Byte mon[256];
FormatStr(mon, "\pReceived %i", c);
MonitorText(mon);
}
#endif
if (err) { /* Timeout */
message = TimeoutStr;
++error;
} else
if (c == CAN)
if (canceled) { /* 2 consecutive CANs ==> abort */
err = ABORT;
goto finished;
} else
++canceled;
else {
if (canceled)
c = 0;
switch (c) {
case ACK:
switch (state) {
case TX_HDR:
if (final) { /* End in batch mode */
err = FINE;
goto finished;
}
if (Settings.batch == 1) {
if (err = Negociate(&useCRC,&use1K))
goto finished;
MakeInfo(useCRC, use1K, vers);
}
state = TX_DATA;
case TX_DATA:
message = VerifyStr;
++blk;
error = 0;
/* Switch buffers */
{
BLOCK *temp = sendbuf;
sendbuf = filebuf;
filebuf = temp;
}
break;
case TX_EOT:
Statistics(mark, Time - time, 0);
error = 0;
final = TRUE;
eof = 0;
UpdateProgress(0,0,0,0,0,EmptyStr);
NameProgress(EmptyStr);
if (Settings.batch) {
if (err = Negociate(&useCRC,
&use1K))
goto finished;
MakeInfo(useCRC, use1K, vers);
blk = 0;
HeaderBlock(sendbuf, EmptyStr,
0, useCRC);
block = 1;
state = TX_HDR;
} else { /* No batch */
err = FINE;
goto finished;
}
break;
}
break;
case NAK:
message = RetryStr;
++error;
break;
default:
message = InvalidStr;
++error;
break;
}
}
} while (c == CAN);
if (error >= RETRY) { /* Check error counter */
err = TIMEOUT;
goto finished;
}
} while (TRUE);
/* Come here if error or finished (err == 0) */
finished:
RemoveCancelDialog();
if (err)
Statistics(mark, Time - time, err);
if (vers)
BinCloseRead();
else
FSClose(ref);
SerialReset(Settings.portSetup);
SerialHandshake(Settings.handshake);
SetItemStyle(GetMenu(FILE), TRANSMIT, 0);
Transfer = 0;
#ifdef MONITOR
MonitorText("\pEnd of X-Modem transmit");
MonitorClose();
#endif
return err;
}